package base.service.impl;

import base.entity.OpenedFile;
import base.entity.regitem.FileRegItem;
import base.entity.regitem.RegItem;
import base.factory.impl.RegItemFactory;
import base.list.FatSingleton;
import base.list.OpenedFilesSingleton;
import base.service.DirectoriesServer;
import base.service.FileService;
import base.util.ByteFileIOUtils;
import base.util.XMLUtils;
import base.view.DirectoriesView;
import test.FormatDisk;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Semaphore;

/**
 * @zXPackgeName: base.service.impl
 * @ClassName: FileServiceImpl
 * @Author: z8932
 * Date: 2019/12/5 18:47
 * project name: OperationSystem
 * @Version:
 * @Description: 使用了大量的try-catch 是为了能继续往下执行
 */
public class FileServiceImpl implements FileService {

    /**
     * 单例的FAT表
     */
    private FatSingleton fatSingleton = FatSingleton.INSTANCE;
    /**
     * 单例的已打开文件表
      */
    private OpenedFilesSingleton openedFilesSingleton = OpenedFilesSingleton.INSTANCE;
    /**
     * 磁盘文件
     */
    private File diskFile = new File(XMLUtils.getFilePath("disk"));
    /**
     * 文件可用起始磁盘盘块
     */
    private static final int AVAILABLE_FAT_NUMBER = 3;
    /**
     * 文件结束符
     */
    private static final byte END_OF_FILE_CHAR = (byte)(-1);
    /**
     * 只读文件
     */
    private static final int ONLY_READABLE = 0x01;
    /**
     * 普通文件
     */
    private static final int READABLE_AND_WRITEABLE = 0x04;
    /**
     * 不是文件
     */
    private static final int NOT_FILE = 0x08;
    /**
     * 信号量，读是生产者，写是消费者
     */
    private Semaphore counter = new Semaphore(0);
    /**
     * 一个盘块有多少字节
     */
    private static final int BLOCK_BYTES = 64;

    /**
     * 创建文件
     * 1、创建缓存文件登记项
     * 2、将登记项和修改后的FAT表写入磁盘，在文件的末尾添加结束符
     * 注意:只读文件不可创建，或目录
     * 名字合法性、类型名合法性，
     * 检查磁盘空间/目录空间
     * 名字和类型首部有空格会被去掉
     * @param nameAndType 文件名字和类型  例如a.txt，用.分割
     * @param property 文件属性
     * @param blockNum 当前目录盘块号
     * @return 创建结果
     */
    @Override
    public boolean creatFile(String nameAndType, int property, int blockNum) {
        // 只读文件不可以创建
        if ((property & ONLY_READABLE) > 0) {
            try {
                throw new Exception("不可以创建只读文件");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        } else if ((property & NOT_FILE) > 0) {
            try {
                throw new Exception("不可以创建目录属性！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        // 检查文件名字、类型是否合法
        if (!checkNameAndType(nameAndType, blockNum)) {
            try {
                throw new Exception("创建失败！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        // 还原名字
        String[] strings = nameAndType.split("\\.");
        StringBuilder nameSb = new StringBuilder(strings[0].trim());
        // 给名字补足长度
        while (nameSb.length() < FileRegItem.NAME_LENTH) {
            nameSb.append(' ');
        }
        // 还原类型
        StringBuilder typeSb = new StringBuilder();
        if (strings.length > 1) {
            typeSb.append(strings[1].trim());
        }
        // 给类型名补足长度
        while (typeSb.length() < FileRegItem.TYPE_LENGTH) {
            typeSb.append(' ');
        }
        // 获取盘块下的登记项列表
        DirectoriesServer directoriesServer = new DirectoriesServerImpl();
        List<RegItem> regItems = directoriesServer.readABlock(blockNum);
        // 删除空登记项
        regItems.removeIf(regItem -> "$  ".equals(regItem.getName()));
        // 该目录空间已满
        if (regItems.size() >= DirectoriesServer.MAX_REGITEM) {
            try {
                throw new Exception("登记项最高只能有8项！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        int startBlock = -1;
        // 查找起始盘块 其他是系统盘块
        for (int i = AVAILABLE_FAT_NUMBER; i < fatSingleton.size(); i++) {
            // 有可用的盘块
            if (fatSingleton.get(i) == 0) {
                startBlock = i;
                break;
            }
        }
        // 检查磁盘空间是否已满
        if (startBlock == -1) {
            try {
                throw new Exception("磁盘空间已满！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        // 新文件登记项
        FileRegItem fileRegItem =(FileRegItem) RegItemFactory.getInstance().newFileInstance(nameSb.toString(), typeSb.toString(), (byte)(property & 0xff), (byte)(startBlock & 0xff), (byte)(1 & 0xff));
        // 更新目录登记项列表
        regItems.add(fileRegItem);
        // 将登记项和FAT表写进磁盘
        try {
            directoriesServer.writeABlock(blockNum, regItems);
        } catch (IOException e) {
            e.printStackTrace();
        }
        fatSingleton.set(startBlock, -1);
        fatSingleton.writeIntoFile();
        //给盘块号的第一个字节写进文件结束符
        if (!writeEndOfFile(startBlock, 0)) {
            try {
                throw new Exception("创建失败！给文件添加末尾失败！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        System.out.println(fileRegItem);
        System.out.println("创建成功！");
        return true;
    }

    /**
     * 通过绝对路径寻找文件
     * 1、通过“/”分离目录和文件名.类型
     * 2、获取目录登记项列表
     * @param filePath 文件绝对路径
     * @return 文件登记项
     */
    @Override
    public FileRegItem searchFileByPath(String filePath) {
        int pointIndex = filePath.lastIndexOf(".") == -1? filePath.length() : filePath.lastIndexOf(".");
        // 目录路径
        String dirPath = filePath.substring(0, filePath.lastIndexOf("/") + 1);
        // 文件名
        StringBuilder fileName = new StringBuilder(filePath.substring(filePath.lastIndexOf("/") + 1, pointIndex).trim());
        while (fileName.length() < 3) {
            fileName.append(" ");
        }
        // 类型名
        StringBuilder fileType = new StringBuilder();
        // 说明有类型
        if (pointIndex != filePath.length()) {
            String[] strings = filePath.split("\\.");
            if (strings.length > 2) {
                try {
                    throw new Exception("非法字符！多个”.“");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
            fileType.append(strings[1].trim());
        }
        // 补全类型
        while (fileType.length() < 2) {
            fileType.append(" ");
        }
        List<RegItem> regItemList = dir(dirPath);
        // 去空目录项
        regItemList.removeIf(regItem -> "$  ".equals(regItem.getName()));
        System.out.println(fileName.toString() + "filename");
        System.out.println(fileType.toString() + "filetype");
        for (RegItem regItem : regItemList) {
            System.out.println(regItem);
            // 名字类型以及不能是目录
            if (regItem.getName().equals(fileName.toString()) && regItem.getType().equals(fileType.toString()) && (regItem.getProperty() & NOT_FILE) == 0) {
                return (FileRegItem)regItem;
            }
        }
        // 查无则返回空
        try {
            throw new Exception(filePath + "查无此文件！");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *  通过文件路径和登记项打开文件，将打开文件写进已打开文件表
     *  判断是否已打开
     * @param dirPath 当前目录路径
     * @param fileRegItem 文件登记项
     * @return 打开结果
     */
    @Override
    public boolean openFileByRegItem(String dirPath, FileRegItem fileRegItem) {
        // 绝对路径还原
        String name = getAbsolutePath(dirPath, fileRegItem);
//        System.out.println("打开文件" + name);
        if (openedFilesSingleton.search(name) != null) {
            try {
                throw new Exception(name + "打开失败！ 已打开文件！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        OpenedFile openedFile = new OpenedFile(name, fileRegItem.getProperty(), fileRegItem.getBeginFatNum(), fileRegItem.getLength(), (byte)-1, fileRegItem.getBeginFatNum(),(byte)0, fileRegItem.getBeginFatNum(),(byte)0);
        if (openedFilesSingleton.add(openedFile)) {
            return true;
        }
        try {
            throw new Exception("打开失败！登记项已满");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 根据绝对路径打开文件
     * @param path 文件绝对路径
     * @return 打开结果
     */
    @Override
    public boolean openFileByPath(String path) {
        FileRegItem fileRegItem = searchFileByPath(path);
        if (openFileByRegItem(path.substring(0, path.lastIndexOf("/") + 1), fileRegItem)) {
            return true;
        }
        try {
            throw new Exception("打开失败！");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 读取文件
     * 1、判断文件是否可读
     * 2、根据已打开文件，从起始盘块开始一个一个读取字节列表
     * 3、根据字节列表转为字符串
     * @param filePath 文件绝对路径
     * @return 读取到的字符串
     */
    @Override
    public String readFile(String filePath) throws Exception {
        filePath = getAbsolutePath(filePath);
        OpenedFile openedFile = openedFilesSingleton.search(filePath);
        if (openedFile == null) {
            throw new Exception(filePath + "没有已打开文件！");
        }
        if ((openedFile.getProperty() & ONLY_READABLE) == 0 && (openedFile.getProperty() & READABLE_AND_WRITEABLE) == 0) {
            throw new Exception(filePath + "不是一个可读文件！");
        }
        int nextBlock = openedFile.getBeginFatNum();
        // 开始读取文本
        List<Byte> byteList = new ArrayList<>();
        do {
            openedFile.setRead_dnum((byte) nextBlock);
            byteList.addAll(Objects.requireNonNull(readOneBlock(openedFile)));
            nextBlock = fatSingleton.get(nextBlock);
        } while (nextBlock != -1);
        byte[] bytes = new byte[byteList.size()];
        for (int i = 0; i < byteList.size(); i++) {
            bytes[i] = byteList.get(i);
        }
        // 给写指针初始化
        openedFile.setRead_dnum(openedFile.getRead_dnum());
        openedFile.setRead_bnum(openedFile.getRead_bnum());
        // 设置已打开文件操作为读
        openedFile.setOperationType((byte) 0);
        counter.release();
        return new String(bytes, StandardCharsets.UTF_8);
    }

    /**
     * 写文件
     * PS: 读写操作必须在同一个服务器类执行保证同步
     * 第一次打开文件读就行
     * 写完之后重新展示页面需要重新进行一次读
     * 1、判断文件是否可写
     * 2、判断文件是否在读状态
     * 3、文件在写入前已经是读过了，根据读指针初始化写指针
     * 4、根据写指针将字符串写进文件
     * @param text 添加的文本
     * @param filePath 文件绝对路径
     * @return 写文件结果
     */
    @Override
    public boolean writeFile(String text, String filePath) throws InterruptedException {
        counter.acquire();
        filePath = getAbsolutePath(filePath);
        OpenedFile openedFile = openedFilesSingleton.search(filePath);
        if (openedFile == null) {
            try {
                throw new Exception(filePath + "没有已打开文件！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        } else if ((openedFile.getProperty() & READABLE_AND_WRITEABLE) ==0) {
            try {
                throw new Exception(filePath + "不是可写文件！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        } else if (openedFile.getOperationType() != 0) {
            try {
                throw new Exception("文件没有被读！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        // 更改已打开文件的状态
        openedFile.setOperationType((byte) 1);
        // 文本长度
        int bytesLenth = text.getBytes().length;
        // 根据盘块剩余可读写空间和文本的长度来写文本
        int begin = 0;
        int end = Math.min(BLOCK_BYTES - openedFile.getWrite_bnum(), bytesLenth);
        // 文件长度
        int openedFileLength = openedFile.getLength();
        // 将文本转为字节数组
        Byte[] textBytes = new Byte[bytesLenth];
        System.out.println("文本长度字节：" + bytesLenth);
        byte[] temp = text.getBytes(StandardCharsets.UTF_8);
        for (int i = 0; i < temp.length; i++) {
            textBytes[i] = temp[i];
        }
        ArrayList<Byte> textByteList = new ArrayList<>(Arrays.asList(textBytes));
        while (end != begin) {
            int skipFileBytesLength = 64*openedFile.getWrite_dnum() + openedFile.getWrite_bnum();
            byte[] bytes = new byte[end - begin];
            for (int i = 0; i < end - begin; i++) {
                bytes[i] = textByteList.get(begin + i);
            }
            ByteFileIOUtils.write(diskFile, bytes, skipFileBytesLength);
            // 说明盘块全部写完
            if (end - begin + openedFile.getWrite_bnum() == BLOCK_BYTES) {
                // 寻找可用盘块
                int nextBlock = -1;
                for (int i = AVAILABLE_FAT_NUMBER; i < fatSingleton.size(); i++) {
                    if (fatSingleton.get(i) == 0) {
                        nextBlock = i;
                        break;
                    }
                }
                // 磁盘空间已满，直接处理返回
                if (nextBlock == -1) {
                    try {
                        throw new Exception("磁盘空间已满！进行写文件结束操作。");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    // 修改已打开文件信息
                    openedFile.setLength(openedFileLength);
                    openedFile.setWrite_bnum((byte)(BLOCK_BYTES - 1));
                    // 在现盘块的末尾添加结束符
                    writeEndOfFile(openedFile.getWrite_dnum(), openedFile.getWrite_bnum());
                    // 更改登记项长度
                    DirectoriesServer directoriesServer = new DirectoriesServerImpl();
                    try {
                        DirectoriesView view = directoriesServer.dir(filePath.substring(0,filePath.lastIndexOf("/") + 1));
                        List<RegItem> regItemList = dir(filePath.substring(0,filePath.lastIndexOf("/") + 1));
                        for (RegItem regItem : regItemList) {
                            // 指向同一个文件
                            if (regItem.getBeginFatNum().equals(openedFile.getBeginFatNum())) {
                                regItem.setLength((byte) openedFileLength);
                            }
                        }
                        // 根目录
                        Byte fatherBlock = AVAILABLE_FAT_NUMBER - (byte)1;
                        // 登记项所在目录
                        if (view.getItem() != null) {
                            fatherBlock = view.getItem().getBeginFatNum();
                        }
                        directoriesServer.writeABlock(fatherBlock, regItemList);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return false;
                } else {
                    // 更新fat表
                    fatSingleton.set(openedFile.getWrite_dnum(), nextBlock);
                    fatSingleton.set(nextBlock, -1);
                    // 更新写指针
                    openedFile.setWrite_dnum((byte) nextBlock);
                    openedFile.setWrite_bnum((byte) 0);
                    // 更新文件长度
                    openedFileLength++;
                }
            }
            begin = end;
            end = Math.min(end + BLOCK_BYTES - openedFile.getWrite_bnum(), bytesLenth);
        }
        // 修改已打开文件信息
        openedFile.setLength(openedFileLength);
        int nextWrite_bnum = (openedFile.getWrite_bnum() + bytesLenth) % BLOCK_BYTES;
        openedFile.setWrite_bnum((byte)(nextWrite_bnum));
        System.out.println(openedFile);
        // 在现盘块的末尾添加结束符
        writeEndOfFile(openedFile.getWrite_dnum(), openedFile.getWrite_bnum());
        // 更改登记项长度
        DirectoriesServer directoriesServer = new DirectoriesServerImpl();
        try {
            DirectoriesView view = directoriesServer.dir(filePath.substring(0,filePath.lastIndexOf("/") + 1));
            System.out.println("获取目录路径：" + filePath.substring(0,filePath.lastIndexOf("/") + 1));
            List<RegItem> regItemList = dir(filePath.substring(0,filePath.lastIndexOf("/") + 1));
            for (RegItem regItem : regItemList) {
                // 指向同一个文件
                if (regItem.getBeginFatNum().equals(openedFile.getBeginFatNum())) {
                    regItem.setLength((byte) openedFileLength);
                }
            }
            // 根目录
            Byte fatherBlock = AVAILABLE_FAT_NUMBER - (byte)1;
            // 登记项所在目录
            if (view.getItem() != null) {
                fatherBlock = view.getItem().getBeginFatNum();
            }
            directoriesServer.writeABlock(fatherBlock, regItemList);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 关闭文件
     * 1、检查是否打开
     * 2、调用remove方法从已打开文件表移除
     * @param filePath 文件绝对路径
     * @return 关闭结果
     */
    @Override
    public boolean closeFile(String filePath) {
        filePath = getAbsolutePath(filePath);
        OpenedFile openedFile = openedFilesSingleton.search(filePath);
        if (openedFile == null) {
            try {
                throw new Exception("文件没有被打开！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        openedFilesSingleton.remove(filePath);
        return true;
    }

    /**
     * 删除文件
     * 1、将文件添加进已打开文件表，删除后将其从表中移除
     * @param dirPath 当前目录路径
     * @param fileRegItem 文件登记项
     * @return 删除结果
     */
    @Override
    public boolean deleteFile(String dirPath, FileRegItem fileRegItem) {
        // 打开文件（删除也需要打开的，防止重复打开
        if (!openFileByRegItem(dirPath, fileRegItem)) {
            try {
                throw new Exception("请关闭文件再删除文件！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        // 删除登记项
        DirectoriesServer server = new DirectoriesServerImpl();
        try {
            // 获取父目录
            DirectoriesView view = server.dir(dirPath);
            List<RegItem> regItemList = dir(dirPath);
            for (RegItem regItem : regItemList) {
                // 找到登记项，将名字置为空
                if (regItem.getBeginFatNum().equals(fileRegItem.getBeginFatNum())) {
                    regItem.setName("$  ");
                }
            }
            // 根目录
            Byte fatherBlock = AVAILABLE_FAT_NUMBER - (byte)1;
            // 登记项所在目录
            if (view.getItem() != null) {
                fatherBlock = view.getItem().getBeginFatNum();
            }
            // 父目录盘块号， 登记项列表
            server.writeABlock(fatherBlock, regItemList );
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 获取已打开文件
        String name = getAbsolutePath(dirPath, fileRegItem);
        OpenedFile openedFile = openedFilesSingleton.search(name);
        // 删除文件结束符
        byte[] bytes = new byte[1];
        bytes[0] = 0;
        ByteFileIOUtils.write(diskFile, bytes, 64*openedFile.getWrite_dnum()+openedFile.getWrite_bnum());
        // 删除FAT表指针
        int thisBlock = openedFile.getBeginFatNum();
        while (thisBlock != -1) {
            int nextBlock = fatSingleton.get(thisBlock);
            fatSingleton.set(thisBlock, 0);
            thisBlock = nextBlock;
        }
        // 删除完成，关闭文件
        openedFilesSingleton.remove(name);
        return true;
    }

    /**
     * 显示文件登记项内容
     * @param fileRegItem 文件登记项
     * @return 文件登记项
     */
    @Override
    public FileRegItem typeFile(FileRegItem fileRegItem) {
        return fileRegItem;
    }

    /**
     * 修改文件属性
     * @param nameAndType 文件名和类型
     * @param property 文件属性
     * @param dirPath 文件目录路径
     * @param fileRegItem 原文件登记项
     * @return 修改结果
     */
    @Override
    public boolean changeFileType(String nameAndType, byte property, String dirPath, FileRegItem fileRegItem) {
        // 不可以修改已打开文件属性
        if (!openFileByRegItem(dirPath, fileRegItem)) {
            try {
                throw new Exception("请关闭文件后再修改类型！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        DirectoriesServer directoriesServer = new DirectoriesServerImpl();
        try {
            DirectoriesView view = directoriesServer.dir(dirPath);
            List<RegItem> regItemList = dir(dirPath);
            for (RegItem regItem : regItemList) {
                if (regItem.getBeginFatNum().equals(fileRegItem.getBeginFatNum())) {
                    // 根目录
                    Byte fatherBlock = AVAILABLE_FAT_NUMBER - (byte)1;
                    // 登记项所在目录
                    if (view.getItem() != null) {
                        fatherBlock = view.getItem().getBeginFatNum();
                    }
                    // 还原名字
                    String[] strings = nameAndType.split("\\.");
                    StringBuilder nameSb = new StringBuilder(strings[0].trim());
                    // 给名字补足长度
                    while (nameSb.length() < FileRegItem.NAME_LENTH) {
                        nameSb.append(' ');
                    }
                    // 还原类型
                    StringBuilder typeSb = new StringBuilder();
                    if (strings.length > 1) {
                        typeSb.append(strings[1].trim());
                    }
                    // 给类型名补足长度
                    while (typeSb.length() < FileRegItem.TYPE_LENGTH) {
                        typeSb.append(' ');
                    }
                    // 如果名字没有修改,检查类型合法性
                    if (nameSb.toString().equals(fileRegItem.getName())) {
                        if (typeSb.length() > FileRegItem.TYPE_LENGTH) {
                            try {
                                throw new Exception(typeSb.toString() + "类型名长度不得超过2！ 修改失败");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            return false;
                        }
                    } else {
                        // 检查文件名字、类型是否合法
                        if (!checkNameAndType(nameAndType, fatherBlock)) {
                            try {
                                throw new Exception("修改失败！");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            return false;
                        }
                    }
                    regItem.setName(nameSb.toString());
                    regItem.setType(typeSb.toString());
                    regItem.setProperty(property);
                }
            }
            // 根目录
            Byte fatherBlock = AVAILABLE_FAT_NUMBER - (byte)1;
            // 登记项所在目录
            if (view.getItem() != null) {
                fatherBlock = view.getItem().getBeginFatNum();
            }
            directoriesServer.writeABlock(fatherBlock, regItemList);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String name = getAbsolutePath(dirPath, fileRegItem);
        openedFilesSingleton.remove(name);
        return true;
    }

    /**
     * 检查名字、类型是否合法
     * 1、非法字符
     * 2、文件名字长度
     * 3、是否重名
     * 4、类型名字长度
     * @param nameAndType 名字和类型
     * @param blockNum 目录盘块号
     * @return 检查结果
     */
    private boolean checkNameAndType(String nameAndType, int blockNum) {
        // 不可以存在非法字符
        String[] strings = nameAndType.split("\\.");
        if (nameAndType.contains("$") || nameAndType.contains("/") || strings.length > 2) {
            try {
                throw new Exception(nameAndType + "含有非法字符！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        StringBuilder nameSb = new StringBuilder(strings[0]);
        // 检查名字合法性
        if (nameSb.length() == 0 || nameSb.length() > FileRegItem.NAME_LENTH) {
            try {
                throw new Exception(nameSb.toString() + "名字长度不得超过3或等于0！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        // 补全名字，查重名
        while (nameSb.length() < FileRegItem.NAME_LENTH) {
            nameSb.append(" ");
        }
        // 检查类型合法性
        StringBuilder typeSb = new StringBuilder();
        if (strings.length > 1) {
            typeSb.append(strings[1]);
        }
        if (typeSb.length() > FileRegItem.TYPE_LENGTH) {
            try {
                throw new Exception(typeSb.toString() + "类型名长度不得超过2！");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        // 获取盘块下的登记项列表
        DirectoriesServer directoriesServer = new DirectoriesServerImpl();
        List<RegItem> regItems = directoriesServer.readABlock(blockNum);
        for (RegItem regItem : regItems) {
            // 检查是否重名
            if (nameSb.toString().equals(regItem.getName())) {
                try {
                    throw new Exception(regItem.getName() + "有重名文件！");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return false;
            }
        }
        return true;
    }

    /**
     * 给文件的末尾追加结束符
     * @param write_dnum 写指针盘块号
     * @param write_bnum 写指针第几个字节可写
     * @return 追加结果
     */
    private boolean writeEndOfFile(int write_dnum, int write_bnum) {
        try {
            byte[] content = new byte[1];
            content[0] = END_OF_FILE_CHAR;
            ByteFileIOUtils.write(diskFile, content, write_dnum*64 + write_bnum);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取目录下的登记项表
     * @param dirPath 目录路径
     * @return 登记项表
     */
    private List<RegItem> dir(String dirPath) {
        // 去掉目录路径所有空格
        StringBuilder name = new StringBuilder(dirPath.trim());
        // 如果路径中最后没有"/"那么添加
        if (name.lastIndexOf("/") != dirPath.length()-1 || dirPath.length() == 0) {
            name.append("/");
        }
        dirPath = name.toString();
        DirectoriesServer directoriesServer = new DirectoriesServerImpl();
        String[] steps = dirPath.split("/");
        int blockNum = 2; // 从根目录开始
        StringBuilder sb = new StringBuilder(); // 路径
        for(int i = 0; i < steps.length;i++){
            // 目录名字小于3，补全
            while (steps[i].length() < 3) {
                steps[i] = steps[i] + " ";
            }
            boolean hasMatch = false;
            List<RegItem> items = directoriesServer.readABlock(blockNum); // 读取
            for (RegItem item : items){
                if (item.getName().equals(steps[i]) && (item.getProperty() & NOT_FILE) > 0){ // 名称对应的目录
                    hasMatch = true;
                    blockNum = item.getBeginFatNum();
                    sb.append(item.getName()).append('/');
                    break;
                }
            }
            if (!hasMatch){
                try {
                    throw new IOException(sb.toString()+"匹配不到："+steps[i]);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
//        sb.append(steps[steps.length-1]);
//        System.out.println(sb.toString());
//        System.out.println("登记项在第"+blockNum+"块磁盘");
        return directoriesServer.readABlock(blockNum);
    }

    /**
     * 获取文件的绝对路径
     * @param dirPath 目录路径
     * @param fileRegItem 文件登记项
     * @return 绝对路径
     */
    @Override
    public String getAbsolutePath(String dirPath, FileRegItem fileRegItem) {
        // 去掉目录路径所有空格
        StringBuilder name = new StringBuilder(dirPath.trim());
        // 如果路径中最后没有"/"那么添加
        if (name.lastIndexOf("/") != dirPath.length()-1 || dirPath.length() == 0) {
            name.append("/");
        }
        name.append(fileRegItem.getName().trim());
        // 类型为空
        if (!"  ".equals(fileRegItem.getType())){
            name.append(".");
            name.append(fileRegItem.getType().trim());
        }
        return name.toString();
    }

    /**
     * 根据文件绝对路径去除名字类型首尾的空格
     * 路径名一致性
     * @param filePath 文件绝对路径
     * @return 文件绝对路径去除名字类型首尾的空格
     */
    @Override
    public String getAbsolutePath(String filePath) {
        FileRegItem fileRegItem = searchFileByPath(filePath);
        return getAbsolutePath(filePath.substring(0, filePath.lastIndexOf("/") + 1), fileRegItem);
    }

    /**
     * 读取已打开文件一个盘块的字符串
     * @param openedFile 已打开文件
     * @return 该盘块的字节数组
     */
    private List<Byte> readOneBlock(OpenedFile openedFile) {
        List<Byte> byteList = new ArrayList<>();
        byte[] content = new byte[1];
        int block = openedFile.getRead_dnum();
        try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(diskFile))) {
            inputStream.skip(block*64);
            for (int i = 0; i < BLOCK_BYTES; i++) {
                inputStream.read(content);
                // 读取到结束符
                if (content[0] == END_OF_FILE_CHAR) {
                    openedFile.setRead_bnum((byte) i);
                    break;
                }
                byteList.add(content[0]);
            }
            // 只有不在0读到结束符才用设置bnum，其他地方没意义
            return byteList;
        } catch (IOException e) {
            System.err.println("读取文件内容时发生异常：" + diskFile.getAbsolutePath());
            e.printStackTrace();
        }
        return null;
    }

//    public static void main(String[] args) throws Exception {
//        FormatDisk formatDisk = new FormatDisk();
//        formatDisk.format();
//        FileServiceImpl fileService = new FileServiceImpl();
//        DirectoriesServer directoriesServer = new DirectoriesServerImpl();
////        directoriesServer.md("a  ");
////        directoriesServer.md("a  /b  ");
//        fileService.creatFile("a", READABLE_AND_WRITEABLE, 2);
//        String path = "/a";
//        path = fileService.getAbsolutePath(path);
//        System.out.println(path + "就是这里");
//        FileRegItem fileRegItem = fileService.searchFileByPath(path);
//        System.out.println(fileRegItem);
//        List<RegItem> regItemList = fileService.dir(path.substring(0, path.lastIndexOf("/") + 1));
//        System.out.println("创建后");
//        regItemList.forEach(System.out::println);
////        fileService.deleteFile(path.substring(0, path.lastIndexOf("/")), fileRegItem);
////        regItemList = fileService.dir(path.substring(0, path.lastIndexOf("/")));
////        System.out.println("删除后");
////        regItemList.stream().forEach(System.out::println);
//        // 相对路径成功,重复打开测试成功
////        for (int i = 0; i < 1; i++) {
////            if (fileService.openFileByRegItem(path.substring(0,path.lastIndexOf("/") + 1), fileRegItem)) {
////                System.out.println("ok");
////            } else {
////                System.out.println("error");
////            }
////        }
////        System.out.println(OpenedFilesSingleton.INSTANCE.search(path));
////         绝对路径成功
//        fileService.openFileByPath(path);
//        String name = fileService.getAbsolutePath(path.substring(0, path.lastIndexOf("/")), fileRegItem);
//        System.out.println(name);
////        String text;
////        // 打开文件，写操作，读
////        text = fileService.readFile(name);
////        System.out.println(text);
////        // 写
////        fileService.writeFile("你妈", name);
////        text = fileService.readFile(name);
////        System.out.println(text);
//        OpenedFile openedFile = OpenedFilesSingleton.INSTANCE.search(name);
//        System.out.println(openedFile);
//        fileService.closeFile(name);
//        fileService.changeFileType("a.", (byte) ONLY_READABLE, path.substring(0, path.lastIndexOf("/")), fileRegItem);
//        regItemList = fileService.dir(path.substring(0, path.lastIndexOf("/")));
//        System.out.println("修改后");
//        regItemList.forEach(System.out::println);
//    }
}
